/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.flexengine.input;

import com.flexengine.cursor.CursorManager;
import com.flexengine.cursor.GameCursor;
import com.flexengine.engine.FlexEngine;
import com.flexengine.logger.Logger;
import com.flexengine.view.GameWindow;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.util.HashMap;
import java.util.Map;

/**
 * @author Luann R. Athayde
 * @version 1.0
 * @since 1.0
 */
public class InputManager implements KeyListener, MouseListener, MouseWheelListener, MouseMotionListener, WindowListener {

    private final Map<Integer, Integer> keyEvents;
    private final Map<Integer, Integer> mouseEvents;
    private Integer mouseX = 0;
    private Integer mouseY = 0;
    private int mouseWheelAmount = 0;
    private int mouseWheelDirection = MOUSE_WHEEL_NONE_DIRECTION;

    public InputManager() {
        keyEvents = new HashMap<>();
        mouseEvents = new HashMap<>();
    }

    private volatile Integer   lastKeyCode = null;
    private volatile Character lastKeyChar = null;

    @Override
    public void keyPressed(KeyEvent ke) {
        keyEvents.put(ke.getKeyCode(), KEY_PRESSED);
        ke.consume();
    }
    
    @Override
    public void keyTyped(KeyEvent ke) {
        keyEvents.put(ke.getKeyCode(), KEY_TYPED);
        ke.consume();
        if( ke.getKeyCode()!=VK_SHIFT && ke.getKeyCode()!=VK_CONTROL && ke.getKeyCode()!=VK_META && ke.getKeyCode()!=VK_ALT && ke.getKeyCode()!=VK_ALT_GRAPH ) {
            lastKeyChar = ke.getKeyChar();
            lastKeyCode = ke.getKeyCode();
        }
    }

    @Override
    public void keyReleased(KeyEvent ke) {
        keyEvents.put(ke.getKeyCode(), KEY_RELEASED);
        if( ke.getKeyCode()==VK_P && ke.isControlDown() ) {
            FlexEngine.getInstance().pause();
        }
        if( ke.getKeyCode()==VK_ESCAPE && isStopOnScape() ) {
            FlexEngine.getInstance().stop();
        }
        if( ke.getKeyCode()==VK_F11 ) {
            FlexEngine.getInstance().setShowFps(!FlexEngine.getInstance().isShowFps());
        }
        if( ke.getKeyCode()==VK_F12 ) {
            if( Logger.getDebugLevel()!=Logger.DEBUG_ALL ) {
                Logger.setDebugLevel(Logger.DEBUG_ALL);
            } else {
                Logger.setDebugLevel(Logger.DEBUG_NONE);
            }
        }
        if( ke.isControlDown() && ke.getKeyCode()==VK_PRINTSCREEN ) {
            GameWindow.captureScreenShot(FlexEngine.getInstance().getGameName());
        }
        ke.consume();
        lastKeyChar = null;
        lastKeyCode = null;
    }

    @Override
    public void mouseClicked(final MouseEvent me) {
        mouseEvents.put(me.getButton(), MOUSE_CLICKED);
        new Thread(new Runnable() {
            @Override
            public synchronized void run() {
                try {
                    this.wait(25);
                    mouseEvents.put(me.getButton(), MOUSE_RELEASED);
                } catch (InterruptedException e) {
                    Logger.logMessage(getClass()+" - mouseClicked(): "+e);
                }
            }
        }).start();
    }

    @Override
    public void mousePressed(MouseEvent me) {
        mouseEvents.put(me.getButton(), MOUSE_PRESSED);
        me.consume();
    }

    @Override
    public void mouseReleased(MouseEvent me) {
        mouseEvents.put(me.getButton(), MOUSE_RELEASED);
        me.consume();
    }

    @Override
    public void mouseEntered(MouseEvent me) {
        me.consume();
    }

    @Override
    public void mouseExited(MouseEvent me) {
        me.consume();
    }

    @Override
    public void mouseWheelMoved(MouseWheelEvent mwe) {
        if (mouseWheelDirection == mwe.getWheelRotation()) {
            mouseWheelAmount += mwe.getScrollAmount();
        } else {
            mouseWheelDirection = mwe.getWheelRotation();
            mouseWheelAmount = mwe.getScrollAmount();
        }
        mwe.consume();
    }

    @Override
    public void mouseDragged(MouseEvent me) {
        mouseMoved(me);
    }

    @Override
    public void mouseMoved(MouseEvent me) {
        mouseX = me.getX();
        mouseY = me.getY();
        GameCursor cursor = CursorManager.getGameCursor();
        cursor.setLocOnScreen(me.getLocationOnScreen());
        cursor.setLocation(me.getPoint());
    }

    @Override
    public void windowOpened(WindowEvent we) {
    }

    @Override
    public void windowClosing(WindowEvent we) {
    }

    @Override
    public void windowClosed(WindowEvent we) {
    }

    @Override
    public void windowIconified(WindowEvent we) {
    }

    @Override
    public void windowDeiconified(WindowEvent we) {
    }

    @Override
    public void windowActivated(WindowEvent we) {
    }

    @Override
    public void windowDeactivated(WindowEvent we) {
    }
    
    private static final InputManager inputManager;
    private static boolean created;
    private static boolean stopOnScape;

    static {
        inputManager = new InputManager();
        created = false;
        stopOnScape = true;
    }

    public static boolean isStopOnScape() {
        return stopOnScape;
    }

    public static void setStopOnScape(boolean stopOnScape) {
        InputManager.stopOnScape = stopOnScape;
    }

    public static void create() {
        if (inputManager != null && !created) {
            Logger.logMessage(inputManager.getClass().getSimpleName()+" - Creating InputManager...");
            GameWindow.getInstance().addKeyListener(inputManager);
            GameWindow.getInstance().addMouseListener(inputManager);
            GameWindow.getInstance().addMouseWheelListener(inputManager);
            GameWindow.getInstance().addMouseMotionListener(inputManager);
            GameWindow.getInstance().addWindowListener(inputManager);
            GameWindow.getInstance().centeredCursor();
            Thread mouseWheelCheck = new Thread(new Runnable() {
                @Override
                public synchronized void run() {
                    try {
                        while (true) {
                            this.wait(100);
                            InputManager.releaseMouseWheel();
                        }
                    } catch (Exception e) {
                    }
                }
            });
            mouseWheelCheck.setDaemon(true);
            mouseWheelCheck.start();
            created = true;
        }
        Logger.logMessage(inputManager.getClass().getSimpleName()+" - InputManager created...");
    }

    public static boolean isKeyDown(Integer keyCode) {
        if ( inputManager.keyEvents.containsKey(keyCode) ) {
            return inputManager.keyEvents.get(keyCode) == KEY_PRESSED;
        }
        return false;
    }

    public static boolean isKeyUp(Integer keyCode) {
        if (inputManager.keyEvents.containsKey(keyCode)) {
            return inputManager.keyEvents.get(keyCode) == KEY_RELEASED;
        }
        return true;
    }
    
    public static Integer getKeyState(Integer keyCode) {
        if(inputManager.keyEvents.containsKey(keyCode)) {
            return inputManager.keyEvents.get(keyCode);
        }
        return -1;
    }
    
    public static Character getLastKeyChar() {
        return inputManager.lastKeyChar;
    }
    public static Integer getLastKeyCode() {
        return inputManager.lastKeyCode;
    }

    public static Integer getMouseX() {
        return inputManager.mouseX;
    }

    public static Integer getMouseY() {
        return inputManager.mouseY;
    }

    public static boolean isMouseCliked(Integer mouseButton) {
        if (inputManager.mouseEvents.containsKey(mouseButton)) {
            if (inputManager.mouseEvents.get(mouseButton) == MOUSE_CLICKED) {
                return true;
            }
        }
        return false;
    }

    public static boolean isMousePressed(Integer mouseButton) {
        if (inputManager.mouseEvents.containsKey(mouseButton)) {
            if (inputManager.mouseEvents.get(mouseButton) == MOUSE_PRESSED) {
                return true;
            }
        }
        return false;
    }

    public static int mouseWheelAmount() {
        return inputManager.mouseWheelAmount;
    }

    public static int mouseWheelDirection() {
        return inputManager.mouseWheelDirection;
    }

    public static boolean hasMouseWheel() {
        return inputManager.mouseWheelDirection != MOUSE_WHEEL_NONE_DIRECTION;
    }

    public static void releaseKey(Integer keyCode) {
        if (inputManager.keyEvents.containsKey(keyCode)) {
            inputManager.keyEvents.put(keyCode, KEY_RELEASED);
        }
        inputManager.lastKeyCode = null;
        inputManager.lastKeyChar = null;
    }

    public static void releaseMouse(Integer mouseButton) {
        if (inputManager.mouseEvents.containsKey(mouseButton)) {
            inputManager.mouseEvents.put(mouseButton, MOUSE_RELEASED);
        }
    }

    public static void releaseMouseWheel() {
        inputManager.mouseWheelAmount = 0;
        inputManager.mouseWheelDirection = MOUSE_WHEEL_NONE_DIRECTION;
    }
    
    public static final int KEY_NONE      = -1;
    public static final int KEY_TYPED     = 400;
    public static final int KEY_PRESSED   = 401;
    public static final int KEY_RELEASED  = 402;
    
    public static final int VK_ENTER = 10;
    public static final int VK_BACK_SPACE = 8;
    public static final int VK_TAB = 9;
    public static final int VK_CANCEL = 3;
    public static final int VK_CLEAR = 12;
    public static final int VK_SHIFT = 16;
    public static final int VK_CONTROL = 17;
    public static final int VK_ALT = 18;
    public static final int VK_PAUSE = 19;
    public static final int VK_CAPS_LOCK = 20;
    public static final int VK_ESCAPE = 27;
    public static final int VK_SPACE = 32;
    public static final int VK_PAGE_UP = 33;
    public static final int VK_PAGE_DOWN = 34;
    public static final int VK_END = 35;
    public static final int VK_HOME = 36;
    public static final int VK_LEFT = 37;
    public static final int VK_UP = 38;
    public static final int VK_RIGHT = 39;
    public static final int VK_DOWN = 40;
    public static final int VK_COMMA = 44;
    public static final int VK_MINUS = 45;
    public static final int VK_PERIOD = 46;
    public static final int VK_SLASH = 47;
    public static final int VK_0 = 48;
    public static final int VK_1 = 49;
    public static final int VK_2 = 50;
    public static final int VK_3 = 51;
    public static final int VK_4 = 52;
    public static final int VK_5 = 53;
    public static final int VK_6 = 54;
    public static final int VK_7 = 55;
    public static final int VK_8 = 56;
    public static final int VK_9 = 57;
    public static final int VK_SEMICOLON = 59;
    public static final int VK_EQUALS = 61;
    public static final int VK_A = 65;
    public static final int VK_B = 66;
    public static final int VK_C = 67;
    public static final int VK_D = 68;
    public static final int VK_E = 69;
    public static final int VK_F = 70;
    public static final int VK_G = 71;
    public static final int VK_H = 72;
    public static final int VK_I = 73;
    public static final int VK_J = 74;
    public static final int VK_K = 75;
    public static final int VK_L = 76;
    public static final int VK_M = 77;
    public static final int VK_N = 78;
    public static final int VK_O = 79;
    public static final int VK_P = 80;
    public static final int VK_Q = 81;
    public static final int VK_R = 82;
    public static final int VK_S = 83;
    public static final int VK_T = 84;
    public static final int VK_U = 85;
    public static final int VK_V = 86;
    public static final int VK_W = 87;
    public static final int VK_X = 88;
    public static final int VK_Y = 89;
    public static final int VK_Z = 90;
    public static final int VK_OPEN_BRACKET = 91;
    public static final int VK_BACK_SLASH = 92;
    public static final int VK_CLOSE_BRACKET = 93;
    public static final int VK_NUMPAD0 = 96;
    public static final int VK_NUMPAD1 = 97;
    public static final int VK_NUMPAD2 = 98;
    public static final int VK_NUMPAD3 = 99;
    public static final int VK_NUMPAD4 = 100;
    public static final int VK_NUMPAD5 = 101;
    public static final int VK_NUMPAD6 = 102;
    public static final int VK_NUMPAD7 = 103;
    public static final int VK_NUMPAD8 = 104;
    public static final int VK_NUMPAD9 = 105;
    public static final int VK_MULTIPLY = 106;
    public static final int VK_ADD = 107;
    public static final int VK_SEPARATER = 108;
    public static final int VK_SEPARATOR = 108;
    public static final int VK_SUBTRACT = 109;
    public static final int VK_DECIMAL = 110;
    public static final int VK_DIVIDE = 111;
    public static final int VK_DELETE = 127;
    public static final int VK_NUM_LOCK = 144;
    public static final int VK_SCROLL_LOCK = 145;
    public static final int VK_F1 = 112;
    public static final int VK_F2 = 113;
    public static final int VK_F3 = 114;
    public static final int VK_F4 = 115;
    public static final int VK_F5 = 116;
    public static final int VK_F6 = 117;
    public static final int VK_F7 = 118;
    public static final int VK_F8 = 119;
    public static final int VK_F9 = 120;
    public static final int VK_F10 = 121;
    public static final int VK_F11 = 122;
    public static final int VK_F12 = 123;
    public static final int VK_F13 = 61440;
    public static final int VK_F14 = 61441;
    public static final int VK_F15 = 61442;
    public static final int VK_F16 = 61443;
    public static final int VK_F17 = 61444;
    public static final int VK_F18 = 61445;
    public static final int VK_F19 = 61446;
    public static final int VK_F20 = 61447;
    public static final int VK_F21 = 61448;
    public static final int VK_F22 = 61449;
    public static final int VK_F23 = 61450;
    public static final int VK_F24 = 61451;
    public static final int VK_PRINTSCREEN = 154;
    public static final int VK_INSERT = 155;
    public static final int VK_HELP = 156;
    public static final int VK_META = 157;
    public static final int VK_BACK_QUOTE = 192;
    public static final int VK_QUOTE = 222;
    public static final int VK_KP_UP = 224;
    public static final int VK_KP_DOWN = 225;
    public static final int VK_KP_LEFT = 226;
    public static final int VK_KP_RIGHT = 227;
    public static final int VK_DEAD_GRAVE = 128;
    public static final int VK_DEAD_ACUTE = 129;
    public static final int VK_DEAD_CIRCUMFLEX = 130;
    public static final int VK_DEAD_TILDE = 131;
    public static final int VK_DEAD_MACRON = 132;
    public static final int VK_DEAD_BREVE = 133;
    public static final int VK_DEAD_ABOVEDOT = 134;
    public static final int VK_DEAD_DIAERESIS = 135;
    public static final int VK_DEAD_ABOVERING = 136;
    public static final int VK_DEAD_DOUBLEACUTE = 137;
    public static final int VK_DEAD_CARON = 138;
    public static final int VK_DEAD_CEDILLA = 139;
    public static final int VK_DEAD_OGONEK = 140;
    public static final int VK_DEAD_IOTA = 141;
    public static final int VK_DEAD_VOICED_SOUND = 142;
    public static final int VK_DEAD_SEMIVOICED_SOUND = 143;
    public static final int VK_AMPERSAND = 150;
    public static final int VK_ASTERISK = 151;
    public static final int VK_QUOTEDBL = 152;
    public static final int VK_LESS = 153;
    public static final int VK_GREATER = 160;
    public static final int VK_BRACELEFT = 161;
    public static final int VK_BRACERIGHT = 162;
    public static final int VK_AT = 512;
    public static final int VK_COLON = 513;
    public static final int VK_CIRCUMFLEX = 514;
    public static final int VK_DOLLAR = 515;
    public static final int VK_EURO_SIGN = 516;
    public static final int VK_EXCLAMATION_MARK = 517;
    public static final int VK_INVERTED_EXCLAMATION_MARK = 518;
    public static final int VK_LEFT_PARENTHESIS = 519;
    public static final int VK_NUMBER_SIGN = 520;
    public static final int VK_PLUS = 521;
    public static final int VK_RIGHT_PARENTHESIS = 522;
    public static final int VK_UNDERSCORE = 523;
    public static final int VK_WINDOWS = 524;
    public static final int VK_CONTEXT_MENU = 525;
    public static final int VK_FINAL = 24;
    public static final int VK_CONVERT = 28;
    public static final int VK_NONCONVERT = 29;
    public static final int VK_ACCEPT = 30;
    public static final int VK_MODECHANGE = 31;
    public static final int VK_KANA = 21;
    public static final int VK_KANJI = 25;
    public static final int VK_ALPHANUMERIC = 240;
    public static final int VK_KATAKANA = 241;
    public static final int VK_HIRAGANA = 242;
    public static final int VK_FULL_WIDTH = 243;
    public static final int VK_HALF_WIDTH = 244;
    public static final int VK_ROMAN_CHARACTERS = 245;
    public static final int VK_ALL_CANDIDATES = 256;
    public static final int VK_PREVIOUS_CANDIDATE = 257;
    public static final int VK_CODE_INPUT = 258;
    public static final int VK_JAPANESE_KATAKANA = 259;
    public static final int VK_JAPANESE_HIRAGANA = 260;
    public static final int VK_JAPANESE_ROMAN = 261;
    public static final int VK_KANA_LOCK = 262;
    public static final int VK_INPUT_METHOD_ON_OFF = 263;
    public static final int VK_CUT = 65489;
    public static final int VK_COPY = 65485;
    public static final int VK_PASTE = 65487;
    public static final int VK_UNDO = 65483;
    public static final int VK_AGAIN = 65481;
    public static final int VK_FIND = 65488;
    public static final int VK_PROPS = 65482;
    public static final int VK_STOP = 65480;
    public static final int VK_COMPOSE = 65312;
    public static final int VK_ALT_GRAPH = 65406;
    public static final int VK_BEGIN = 65368;
    public static final int VK_UNDEFINED = 0;
    public static final char CHAR_UNDEFINED = '\uffff';
    public static final int KEY_LOCATION_UNKNOWN = 0;
    public static final int KEY_LOCATION_STANDARD = 1;
    public static final int KEY_LOCATION_LEFT = 2;
    public static final int KEY_LOCATION_RIGHT = 3;
    public static final int KEY_LOCATION_NUMPAD = 4;
    public static final int MOUSE_CLICKED = 500;
    public static final int MOUSE_PRESSED = 501;
    public static final int MOUSE_RELEASED = 502;
    public static final int MOUSE_NOBUTTON = 0;
    public static final int MOUSE_BUTTON1 = 1;
    public static final int MOUSE_BUTTON2 = 2;
    public static final int MOUSE_BUTTON3 = 3;
    public static final int MOUSE_WHEEL_FRONT_DIRECTION = -1;
    public static final int MOUSE_WHEEL_NONE_DIRECTION = 0;
    public static final int MOUSE_WHEEL_BACK_DIRECTION = 1;
    
}
